package edu.upenn.seas.peerreview.smui.variants

import edu.upenn.seas.peerreview.Id

import scala.collection.immutable.Stack
import scala.swing._
import scala.swing.event._
import java.awt.Color
import javax.swing.{ UIManager, SwingUtilities }
import java.util.Date
import java.text.SimpleDateFormat

/**
 * An application decider draws non-deterministic input from a user
 * interface, created with the Scala Swing library.
 * 
 * <p>The GUI uses the IdMapper trait to present "nice" naming for opponents.
 */
class SwingDecider() extends SimpleGUIApplication with Decider 
    with IdMapper {
  
  /**
   * Reference functions to retrieve our own, and opponents', boards, and quit game.
   */
  private var myBoard: () => Board = null
  private var getBoard: Id => Board = null
  
  /**
   * Game-specific data: the board size, ships, and (entire) list of opponents.
   */
  private var refBoard: Board = null
  private var opponents: List[Id] = List()
  private var shipSizes: List[Int] = List()
  
  /**
   * A list of game statistics upon game end: (Hits, Sinks, Defeats, Win/Lose)
   */
  private var stats: List[Int] = Nil
  
  /**
   * The current game panel being shown in the GUI. This is dependent on the
   * current "state" of the game (start, ship placing, play, end game).
   */
  private var currentPanel: StatePanel = null
  
  /**
   * If there is a second game waiting to be started.
   */
  private var enableNewGame: Boolean = false
  
  /**
   * A panel that logs messages, and can be switched into context atop the
   * regular game panel.
   */
  private var logs = new ScrollPane() with Logs {
    private var messages: List[String] = Nil
    private val formatter = new SimpleDateFormat("h:mm:ss a")
    private val myText = new TextArea() {
      wordWrap = true
      editable = false
      background = java.awt.Color.WHITE
      border = Swing.EmptyBorder(10)
    }
    contents = myText
    
    /**
     * Add a message to the log console.
     */
    def log(msg: String): Unit = {
      messages ::= "%s -- %s".format(formatter format (new Date), msg)
      myText.text = (messages.reverse mkString "\n")
    }
  }
  
  /**
   * Hook to show the log panel.
   */
  def showLogPanel() = {
    mainFrame.contents = new BorderPanel() {
      add(stateManager, BorderPanel.Position.North)
      add(logs, BorderPanel.Position.Center)
      add(statusBar, BorderPanel.Position.South)
      SwingUtilities.updateComponentTreeUI(this.peer)
    }
  }
  
  /**
   * Hook to restore the original game panel (if the log panel is being shown.)
   */
  def restore() = {
    mainFrame.contents = makeNewPanel(currentPanel)
  }
  
  /**
   * Make a new child panel that is framed by the stateManager and statusBar.
   * @param child the new child content panel
   */
  private def makeNewPanel(child: StatePanel) = new BorderPanel() {
    currentPanel = child
    add(stateManager, BorderPanel.Position.North)
    add(child, BorderPanel.Position.Center)
    add(statusBar, BorderPanel.Position.South)
    SwingUtilities.updateComponentTreeUI(this.peer)
  }
  
  // Create a state manager.
  private lazy val stateManager = new FlowPanel() {
    var currentState: Int = 0
    contents += new Label("<html><b>Current Game Phase:</b></html>")
    contents += new Label("Join Game") with Padding   // 1
    contents += new Label("Place Ships") with Padding // 2
    contents += new Label("Attack!") with Padding     // 3
    contents += new Label("Game Over") with Padding   // 4
  
    /**
     * Change the current state of the game.
     * @param state the numeric state of the game, in range [1, 4]
     */
    def changeState(state: Int) = {
      currentState = state
      state match {
        case e if (e >= 1 && e <= 4) => {
          contents.drop(1).foreach(_.enabled = false)
          contents(e).enabled = true
        }
        case _ => None
      }
    }
  }
  
  // Create a status bar.
  val statusBar = new TextField() with StatusBar {
    editable = false
    border = Swing.EmptyBorder(5)
    background = new java.awt.Color(236, 233, 216)
    text = ""
    /**
     * Writes a message to the status bar.
     */
    def update(msg: String) = {
      text = msg
    }
  }
  
  // The main window/frame.
  val mainFrame = new MainFrame {
    UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName())
    title = "PR Battleship" 
    minimumSize = new java.awt.Dimension(850, 650)
    preferredSize = minimumSize
    contents = new ScrollPane()
    reactions += {
      case WindowClosed(_) => { 
        System.exit(0) 
        // TODO: WindowClosing event (which System.exits) isn't called upon by
        // this.dispose() -- why? Simulate on WindowClosed instead for File > Quit.
      }
    }
  }
  
  // Menu bar.
  val actions = new MenuActions(() => { currentPanel }, mainFrame)
  mainFrame.menuBar = new MenuBar() {
    contents += new Menu("File") {
      contents += new MenuItem(actions.aboutPRAction)
      contents += new MenuItem("Quit Game") {
        reactions += {
          case ButtonClicked(e) => {
            Dialog.showConfirmation(currentPanel, 
            "Are you sure you want to quit? This destroys the current game.",
            "Quit Game", Dialog.Options.YesNo) match {
              case Dialog.Result.Yes => mainFrame.dispose()
              case _ => None
            }
          }
        }
      }
    }
    contents += new Menu("Game") {
      contents += new MenuItem(actions.rulesAction)
      contents += new Separator
      val viewMessageLogButton = new CheckMenuItem("View Message Log") {
        reactions += {
          case ButtonClicked(e) => showLogPanel()
        }
      }
      val viewGamePanelButton = new CheckMenuItem("View Game Panel") {
        selected = true // To start.
        reactions += {
          case ButtonClicked(e) => restore()
        }
      }
      val selectionGroup = new ButtonGroup(viewMessageLogButton, viewGamePanelButton)
      contents += viewMessageLogButton
      contents += viewGamePanelButton
    }
  }
  
  // Start up the GUI window.
  def top = mainFrame
  this.main(null)
  changeState(1)
  
  /**
   * Cause an application-wide state change.
   */
  def changeState(state: Int): Unit = {
    val formerPanel = currentPanel
    logs.log("Requesting application state change to: " + state)
    state match {
      case 1 => {
          stateManager.changeState(1)
          mainFrame.contents = makeNewPanel(new JoinPanel(statusBar, this))
      }
      case 2 => {
        enableNewGame = false // Ensure this is false; e.g. in first game.
        stateManager.changeState(2)
        assert(shipSizes != Nil)
        mainFrame.contents = 
            makeNewPanel(new PlaceShipPanel(statusBar, refBoard.size, shipSizes, this))
      }
      case 3 => {
        stateManager.changeState(3)
        mainFrame.contents = makeNewPanel(new PlayPanel(statusBar, 
                                                        logs,
                                                        opponents,
                                                        refBoard.size,
                                                        shipSizes,
                                                        myBoard,
                                                        getBoard,
                                                        this))
      }
      case 4 =>  {
          stateManager.changeState(4)
          mainFrame.contents = makeNewPanel(new GameOver(statusBar, stats, this))
          
          // If previously received new game, but just now changed state,
          // immediate enable state change.
          if (enableNewGame) {
            currentPanel.enableStateChange()
            logs.log("Enabling state change to the brand new game.")
          }
      }
    }
    if (formerPanel != null)
      formerPanel.synchronized {
        formerPanel.notify
      }
  }
  
  /**
   * Resets the panel states to a new game in the tournament IF the flag is set
   * stating that a new game is available.
   * 
   * <p>Assumes that the game has already received the configurations for the new game.
   */
  def beginNewGame() {
    logs.log("Begin new game function called.")
    if (enableNewGame) {
      stats = Nil
      changeState(2)
    }
  }
  
  /*****************************************
   * Game Play Hooks for Decider interface
   ****************************************/
  
  override def onRegister(getMyBoard: () => Board, getOpponentBoard: Id => Board) {
    myBoard = getMyBoard
    getBoard = getOpponentBoard
    logs.log("onRegister called.")
  }
  
  override def onNewGame(opponents: List[Id], boardSize: Point) {
    logs.log("onNewGame called: opponents: %s and size %s"
           .format(opponents.toString, boardSize))
    refBoard = Board.makeBoard(boardSize)
    this.opponents = opponents
    makeMapping(opponents)
    
    // A new game is available.
    enableNewGame = true
    
    // If we're already at the end screen of a previous game, enable switch.
    if (stateManager.currentState == 4) {
      currentPanel.enableStateChange()
    }
  }
  
  override def placeShips(boardSize: Point, shipSizes: List[Int]): List[(Point, Point)] = {
    this.shipSizes = shipSizes
    
    if (stateManager.currentState == 1)
      currentPanel.enableStateChange()
    else if (stateManager.currentState != 2) {
      currentPanel.synchronized {
        currentPanel.wait // Will wait here until the user gets to panel 2.
      }
      return placeShips(boardSize, shipSizes)
    }
     
    // Use while-loop instead of recursion to avoid stack-overflow. (TODO: could tail recurse this.)
    while (!currentPanel.isInstanceOf[PlaceShipPanel] ||  
           currentPanel.asInstanceOf[PlaceShipPanel].sols.length != shipSizes.length+1) {
      Thread.sleep(500)
    }

    val res = currentPanel.asInstanceOf[PlaceShipPanel].sols.map(tup => tup._1).toList.drop(1)
    logs.log("Ship placements sent:\n %s".format(res))
    res
  }
  
  override def makeMove(opps: List[Id] ): (Id, Point) = {        
    if (currentPanel.isInstanceOf[PlayPanel]) {
      logs.log("Requesting move...")
      currentPanel.asInstanceOf[PlayPanel].update()
      currentPanel.asInstanceOf[PlayPanel].newTurn()
      val res = currentPanel.asInstanceOf[PlayPanel].getNextMove()
      logs.log("Recorded move: %s".format(res))
      statusBar.update("Move sent! Waiting...")
      currentPanel.asInstanceOf[PlayPanel].report.reset()
      res
    } else if (stateManager.currentState == 2) {
      currentPanel.enableStateChange()
      currentPanel.synchronized {
        currentPanel.wait
      }
      return makeMove(opps)
    } else { // Mid-state change, try again.
      currentPanel.synchronized {
        currentPanel.wait
      }
      return makeMove(opps)
    }
  }
  
  override def onOpponentMoved(opp:Id) { 
    if (currentPanel.isInstanceOf[PlayPanel])
       currentPanel.asInstanceOf[PlayPanel].madeMove(opp)
    logs.log("\tonOpponentMoved - %s has submitted his/her move.".format(mapId(opp)))
  }

  override def onBeingFiredAt(from: Id, loc:Point) { 
    logs.log("\tonBeingFiredAt - %s fired at your point %s!".format(mapId(from), loc))
  } 
  
  override def onFiredAtReport(oppFrom:Id, loc:Point, result:FireResultType) { 
    logs.log("onFiredAtReport - BOOM! %s fired at YOU at %s with result: %s"
        .format(mapId(oppFrom), loc, result))  
    report("%s fired at you! (%s, %s)".format(mapId(oppFrom), loc, result))
    if (result == Defeat) {
      report("You lost!")
      logs.log("onFiredAtReport - Message of your defeat received.")
    }
  }
  
  override def onOpponentFireReport(oppFrom:Id, target:Id, loc:Point, 
      result:FireResultType) { 
    logs.log("onOpponentFireReport - INFO: %s fired at %s, at %s with result: %s"
        .format(mapId(oppFrom), mapId(target),  loc, result))
    report("%s fired at %s. (%s, %s)"
        .format(mapId(oppFrom), mapId(target), loc, result))
  }
  
  override def onYourFireReport(target:Id, loc:Point, result:FireResultType) { 
    logs.log("onYourFireReport - RESULT: You fired at %s, at %s with result: %s"
        .format(mapId(target), loc, result))  
    report("* You fired at %s (%s, %s)".format(mapId(target), loc, result))
  }
  
  /**
   * Helper method that writes actions into the "Last Turn" box.
   */
  private def report(msg: String) = {
    if (currentPanel.isInstanceOf[PlayPanel])
       currentPanel.asInstanceOf[PlayPanel].report.report(msg)
  }

  override def onPlayerResult(opp:Id, hits:Int, sinks:Int, kills:Int, hasWon:Boolean) {
    if (currentPanel.isInstanceOf[PlayPanel])
       currentPanel.asInstanceOf[PlayPanel].markPlayerOut(opp)
    logs.log("onPlayerResult - PLAYER FINISHED: %s".format(mapId(opp)))
    report("> Game over for %s!".format(mapId(opp)))
  }  
  
  override def onYourFinish(hits:Int, sinks:Int, kills:Int, hasWon:Boolean) {
    logs.log("onYourFinish - Received your finish statistics: %d %d %d".format(hits, sinks, kills))
    stats = List(hits, sinks, kills, if (hasWon) 1 else 0)
    if (stateManager.currentState == 3) {
      currentPanel.enableStateChange()
    }
  }
  
  override def onGameOver() {
    statusBar.update("Game over!")
    logs.log("onGameOver - Game over message received.")
    if (stateManager.currentState == 3) {
      currentPanel.enableStateChange()
    }
  }
  
  override def onTournamentOver() {
    statusBar.update("onTournamentOver - Tournament over! Initiating audits.")
  }
}

/**
 * Exposed interface for the status bar.
 */
trait StatusBar {
  def update(msg: String): Unit
}


/**
 * Exposed interface for the logger panel.
 */
trait Logs {
  def log(msg: String): Unit
}

/**
 * Common interface for actionable game state panels.
 */
trait StatePanel extends Component {
  def enableStateChange(): Unit
}      

/**
 * A button class that has marked, ship and unmarked positions.
 * @param i
 * @param j
 * @param cb the callback function upon click
 */
class MarkedButton(i: Int, j: Int, cb: (Int, Int) => Unit) extends Button() {
  text = "<html><b>    </b></html>"
  reactions += {
    case ButtonClicked(e) => if (enabled) cb(i, j)
  }
  var marked = false
  def shipify(ship: Vessel): Unit = {
    enabled = false
    marked = false
    text = "<html><font color=blue><b>S%d</b></font></html>".format(ship.size)
  }
  def reset() = {
    enabled = true
    marked = false
    text = "    "
  }
  def mark() = {
    enabled = true
    marked = true
    text = "S"
  }
}

/**
 * An ocean grid display that (at any one given time) is meant to place
 * a particular ship of a given size onto a given board.
 * @param sbar the status bar
 * @param board the initial board state
 * @param shipSize the size of the ship to be placed
 * @param parent the parent PlaceShipPanel
 */
class ShipPlacer(sbar: StatusBar, var board: Board, var shipSize: Int, 
     parent: PlaceShipPanel) extends GridPanel(board.height, board.width) {   
      
  type Placement = (Point, Point)
  border = Swing.EmptyBorder(15)
  
  /**
   * Helper for getting reference to the button at (i, j)
   */
  private def getButton(i: Int, j: Int) = buttons(j*board.width + i)
    
  /**
   * Verifies that a ship placement is feasible. If so, stores the placement
   * and resulting board state in instance variable.
   * @param x proposed head of ship
   * @param y
   */
  private def tryPlacement(x: Int, y: Int): Unit = {
    
    // If finished placing all ships, do nothing on button click.
    if (shipSize == 0) return
    
    val loc = (x, y)
    val add = if (parent.currentDir.isHorizontal) (1, 0) else (0, 1)
    val points = (for (i <- 0 to shipSize-1) 
        yield Point(loc._1 + i * add._1, loc._2 + i * add._2)).toList
    
    if (!points.forall(board.examine(_) == CellStatus.Empty)) {
      sbar.update("Invalid placement -- off the board or overlaps!")
      return
    }
    
    buttons.foreach(b => if (b.marked) b.reset())
    var curBoard = board
    val v = Vessel(shipSize)
    points.foreach(p => {
      curBoard = curBoard + (p.x, p.y, CellStatus.Ship(v))
      getButton(p.x, p.y).mark()
    })
    lastMove = ((points.head, points.last), curBoard)
    parent.nextButton.enabled = true
    sbar.update("Placed! Press 'next' to continue, or click again to replace.")
  }
  
  /**
   * Update this panel to accept a new target ship size.
   * @param newBoard the new base board state
   * @param newSize the new ship size to be placed
   */  
  def update(newBoard: Board, newSize: Int): Unit = {
    board = newBoard
    shipSize = newSize
    for (j <- 0 until board.height; i <- 0 until board.width) {
      board.examine(i, j) match {
        case CellStatus.Ship(v) => getButton(i, j).shipify(v)
        case _ => getButton(i, j).reset()
      }
    }
  }
  
  /**
   * The last (feasible) move chosen on this board, accessible by the parent panel.
   */
  private[variants] var lastMove: (Placement, Board) = null
  
  // The list of buttons.
  private val buttons: List[MarkedButton] = (for (j <- 0 until board.height; 
                                                  i <- 0 until board.width) 
    yield {
      val button = new MarkedButton(i, j, tryPlacement _)
      board.examine(i, j) match {
        case CellStatus.Ship(v) => { button.shipify(v); v}
        case _ => 
      }
      button
  }).toList
  buttons.foreach(contents += _)
}

/**
 * A panel that queries the user for a set of ship placements, permitted
 * backtracking and placement verification.
 * @param sbar the status bar
 * @param size the ocean size
 * @param shipSizes the sizes of the ships to be placed
 * @param parent the parent SwingDecider
 */
class PlaceShipPanel(sbar: StatusBar, size: Point, shipSizes: List[Int], 
    parent: SwingDecider) extends GridPanel(1, 2) with StatePanel {
  
  type Placement = (Point, Point)
  
  // Some temporarily assignments. The later bootstrap will adjust for start state.
  private var current = 0  
  private val shipPlacer = new ShipPlacer(sbar, Board.makeBoard(size), shipSizes(0), this)
  var sols: Stack[ (Placement, Board) ] = (Stack.Empty).push( (null, Board.makeBoard(size)))
  
  // Visual components.
  val currentDir = new ComboBox(List(" Horizontal ", " Vertical ")) {    
    def isHorizontal = this.selection.index == 0
  }
  val backButton = new Button("back")
  val nextButton = new Button("next")
  private val descriptionPanel = new BorderPanel() {
    border = Swing.EmptyBorder(15)
    layoutManager.setVgap(15)
    layoutManager.setHgap(15)
    val label = new Label() {
      text = ""
      opaque = false
      preferredSize = new java.awt.Dimension(250, 25)
      maximumSize = preferredSize
      border = Swing.EmptyBorder(10)
    }
    val icon = new Label() { }
    val continueButton = new Button() {
      text = "Waiting..."
      enabled = false
      visible = false
      reactions += {
        case ButtonClicked(e) => parent.changeState(3)
      }
    }

    add(icon, BorderPanel.Position.Center)
    add(continueButton, BorderPanel.Position.South)
    add(new GridPanel(2, 1) {
      border = Swing.LineBorder(java.awt.Color.black)
      background = java.awt.Color.white
      contents += label
      contents += new Label("Ship Set: " + shipSizes.map(Vessel(_).name).mkString(", ")) { 
        border = Swing.EmptyBorder(15) 
      }
    }, BorderPanel.Position.North)
    
    /**
     * Update the panel to display a new caption and image for a new ship.
     */
    def update(ship: Int): Unit = {
      val s = Vessel(ship)
      label.text = "<html><font size=+1>Please place your %s of size %d.</font></html>"
          .format(s.name, s.size) 
      icon.icon = Swing.Icon(s.source)
      label.repaint
      icon.repaint
    }
    
    /**
     * Update the panel to notify on finished ship placements.
     */
    def waitForGame(): Unit = {
      label.text = "All your ships have been placed! Please wait for opponents."
      icon.icon = Swing.Icon(Images.get("waiting.gif"))
      continueButton.visible = true
      this.repaint
    }
  }
  
  def enableStateChange(): Unit = {
    descriptionPanel.continueButton.text = "Continue"
    descriptionPanel.continueButton.enabled = true
    descriptionPanel.label.text = "Game has started! Press below to continue."
    descriptionPanel.icon.icon = Swing.Icon(Images.get("bombsAway.gif"))
    sbar.update("Press 'continue' to start firing at opponents!")
  }
  
  // Initiate a state change to place a new ship in the sequence.
  private def changeState(num: Int) = num match {
    case e if (e >= 0 && e < shipSizes.length) => {
      current = e
      shipPlacer.update(sols.top._2, shipSizes(current))
      nextButton.enabled = false
      backButton.enabled = if (current == 0) false else true
      descriptionPanel.update(shipSizes(current))
      sbar.update("")
    }
    case e if (e == shipSizes.length) => {
      current = e
      nextButton.enabled = false
      backButton.enabled = false
      // Show the last ship placement, but do not permit changes.
      shipPlacer.update(sols.top._2, 0)
      sbar.update("All ships placed! Now waiting for opponents...")
      descriptionPanel.waitForGame()
    }
    case _ => None
  }
  
  // Callback for back/next buttons.
  private def stateForward(go: Boolean) = go match {
    case true if (current != shipSizes.length) => {  
      val placement = shipPlacer.lastMove
      if (placement._1 != null) {
        sols = sols.push(placement)
        shipPlacer.lastMove = (null, null)
        changeState(current+1)
      } else {
        sbar.update("You must place this ship before continuing.")
      }
    }
    case false if current > 0 => {
      sols = sols.pop
      changeState(current-1)
      sbar.update("Please pick a new spot for this ship!")
    }
    case _ => 
  }
  
  nextButton.reactions += { case ButtonClicked(e) => stateForward(true) }
  backButton.reactions += { case ButtonClicked(e) => stateForward(false) }
  
  changeState(0) // Bootstrap starting display.
  
  contents += new BoxPanel(Orientation.Vertical) {
    border = Swing.EmptyBorder(15)
    contents += shipPlacer
    contents += new FlowPanel() {
        contents += backButton
        contents += currentDir
        contents += nextButton
      }
  }
  contents += descriptionPanel
}

/**
 * A panel that shows the starting splash screen, waits for the game configurations
 * and then presents a button to enter the ship-placing stage.
 * @param sbar status bar
 * @param parent
 */
class JoinPanel(sbar: StatusBar, parent: SwingDecider) extends BorderPanel() 
    with StatePanel {
      
  def enableStateChange(): Unit = {
    playButton.text = "Start"
    playButton.enabled = true
    sbar.update("Press 'Start' to begin the game.")
  }
  
  private val playButton = new Button("Waiting for game configs...") {
    enabled = false
    listenTo(this)
    reactions += {
        case ButtonClicked(e) => parent.changeState(2)
      }
  }
  
  layoutManager.setHgap(15)
  layoutManager.setVgap(15)
  border = Swing.EmptyBorder(15)
  add(new Label("<html><font size=+1>Welcome to <b>Battleship</b>!</font></html>"), 
      BorderPanel.Position.North)
  add(new Label() { icon = Swing.Icon(Images.get("splash.jpg")) }, 
      BorderPanel.Position.Center)
  add(new FlowPanel() { contents += playButton  }, BorderPanel.Position.South)
  sbar.update("Waiting for game configs...")
}

/**
 * A panel that shows the finish image and calculates the score from the final
 * player statistics.
 * @param sbar
 * @param stats
 * @param parent
 */
class GameOver(sbar: StatusBar, var stats: List[Int], parent: SwingDecider) extends
    BorderPanel with StatePanel {
      
  def enableStateChange(): Unit = { 
    nextButton.text = "Start next game in tournament >>" 
    nextButton.enabled = true 
  }
  
  if (stats == Nil) stats = List(-1, -1, -1, 0)
  
  private val statsTable = new Label() {
    border = Swing.EmptyBorder(15)
    opaque = false
    text = ("<html><font size=+1><b>Hits</b>: %d<br><b>Sinks</b>: %d<br><b>Defeats</b>: %s</font></html>")
        .format(stats(0), stats(1), stats(2))
  }
    
  private val nextButton = new Button("End of Tournament") {
    enabled = false
    reactions += {
      case ButtonClicked(e) => parent.beginNewGame()
    }
  }
  
  private val points = List(10, 20, 30)
  private val score = stats.zip(points).foldLeft(0)((sum, i) => sum + i._1*i._2)
  
  border = Swing.EmptyBorder(15)
  add(new GridPanel(1, 2) {
    border = Swing.EmptyBorder(10)
    contents += new Label() { icon = Swing.Icon(
      Images.get(if (stats(3) == 0) "gameover.jpg" else "gameover_win.jpg")
    )}
    contents += new BoxPanel(Orientation.Vertical) {
      contents += new Label("<html><font size=+1>Game over! You %s</font></html>"
        .format(if (stats(3) == 0) "Lost." else "Won!"))
      contents += statsTable
      contents += new Label("<html><font size=+1>Your final score is: %s</font></html>"
          .format(score))
    }
  }, BorderPanel.Position.Center)
  add(new FlowPanel() { contents += nextButton }, BorderPanel.Position.South)
  sbar.update("Thanks for playing!")
}


/**
 * Defines a set of fixed actions for menu items.
 * @param mainPanel the component that the dialogs should be centered around
 *        They must exist with certainty.
 * @param window the main window frame (for quitting)
 */
class MenuActions(mainPanel: () => Component, window: Frame) {
  
  def aboutPRAction: Action = Action("About PR Battleship") {
    Dialog.showMessage(mainPanel(), 
        "<html>This project was implemented in <b>Scala</b> under the guidance of B.C. Pierce.<br>" +
        "Based on: <u>PeerReview: Practical Accountability for Distributed Systems</u><br>" +
        "(Haeberlen, Kuznetsov, Druschel).<br><br>University of Pennsylvania, Spring 2010.</html>",
        "About",
        Dialog.Message.Info, null)
  }

  def rulesAction: Action = Action("Game Rules") {
   var rules: List[String] = Nil
   rules ::= "<i>JOIN:</i> Create a local client and join a game."
   rules ::= "<i>PLACE:</i> Place your set of ships on your gameboard."
   rules ::= "<i>PLAY:</i> Choose one opponent and attack an unknown space."
   rules ::= "<i>FINISH:</i> If all your ships are hit, you're done!"
   rules ::= "Be the last person alive to win!"
   Dialog.showMessage(mainPanel(), 
      "<html>" + (rules.reverse mkString "<br>") + "</html>",
      "Rules",
      Dialog.Message.Info, null)
  }   
}
